import { DBConfig, ItemConfig, MilliSeconds, TableConfig } from './interface'
import { paramChecker, ParamCheckerEnum, dbEnvChecker, optionWithBackup } from './utils'

const DEFAULT_DB_VERSION = 1
const OPTIONAL = true

function customDBConfigChecker (dbConfig:DBConfig):void{
  paramChecker(
    dbConfig,
    ParamCheckerEnum.NotNullObject,
    'dbConfig',
    !OPTIONAL,
  )
  paramChecker(dbConfig.dbName, ParamCheckerEnum.String, 'dbName', !OPTIONAL)
  paramChecker(
    dbConfig.dbVersion,
    ParamCheckerEnum.NonNegativeInteger,
    'dbVersion',
    OPTIONAL,
  )
  paramChecker(
    dbConfig.itemDuration,
    ParamCheckerEnum.NonNegativeInteger,
    'itemDuration of dbConfig',
    OPTIONAL,
  )
  paramChecker(
    dbConfig.tables,
    ParamCheckerEnum.NotNullObject,
    'tables',
    OPTIONAL,
  )
  Object.keys(dbConfig.tables || {}).forEach(tableName => {
    const tableConfig = dbConfig.tables![tableName]
    paramChecker(
      tableConfig.primaryKey,
      ParamCheckerEnum.String,
      'primaryKey',
      OPTIONAL,
    )
    paramChecker(
      tableConfig.itemDuration,
      ParamCheckerEnum.NonNegativeInteger,
      'itemDuration of table',
      OPTIONAL,
    )
    paramChecker(
      tableConfig.indexList,
      ParamCheckerEnum.Array,
      'indexList',
      OPTIONAL,
    );
    (tableConfig.indexList || []).forEach(index => {
      paramChecker(
        index.indexName,
        ParamCheckerEnum.String,
        'indexName',
        !OPTIONAL,
      )
      paramChecker(
        index.unique,
        ParamCheckerEnum.Boolean,
        'unique',
        OPTIONAL,
      )
    })
  })
}

function customDBAddItemsParamChecker (
  items: ItemConfig[],
  tableListInDB: TableConfig[],
) {
  paramChecker(items, ParamCheckerEnum.Array, 'items', !OPTIONAL)
  items.forEach(itemOfTable => {
    paramChecker(
      itemOfTable.tableName,
      ParamCheckerEnum.String,
      "item's tableName",
      !OPTIONAL,
    )
    paramChecker(
      itemOfTable.itemDuration,
      ParamCheckerEnum.NonNegativeInteger,
      "item's itemDuration",
      OPTIONAL,
    )
    const theTable = tableListInDB.filter(
      table => table.tableName === itemOfTable.tableName,
    )[0]
    if (!theTable) {
      throw new Error(`Table ${itemOfTable.tableName} does not exist`)
    } else if (
      theTable.primaryKey !== undefined &&
      Object.getOwnPropertyNames(itemOfTable.item).indexOf(
        theTable.primaryKey,
      ) < 0
    ) {
      throw new Error(`primaryKey is needed for item in table ${itemOfTable.tableName}`)
    }
  })
}

export function idbIsSupported ():boolean {
  try {
    dbEnvChecker()
    return true
  } catch (error) {
    return false
  }
}

export class CustomDB {
  private name: string;
  private version: number;
  private tableList: TableConfig[];
  private itemDuration?: MilliSeconds
  private DB:IDBDatabase
  constructot (dbConfig:DBConfig) {
    // 检查indexDB支持
    dbEnvChecker()
    // 检查传入的参数
    customDBConfigChecker(dbConfig)
    this.name = dbConfig.dbName
    this.version = optionWithBackup(dbConfig.dbVersion, DEFAULT_DB_VERSION)
    this.tableList = Object.keys(dbConfig.tables || {}).map(tableName => {
      return {
        tableName,
        ...dbConfig.tables![tableName],
      }
    })
    this.itemDuration = dbConfig.itemDuration
  }

  async addItems (itemConfigs:ItemConfig[]) {
    const itemDurationOverrider = (
      ofDB:number|undefined,
      ofTable:number|undefined,
      ofItem:number|undefined,
    ) => {
      if (ofItem !== undefined) {
        return ofItem
      } else if (ofTable !== undefined) {
        return ofTable
      } else {
        return ofDB
      }
    }

    customDBAddItemsParamChecker(itemConfigs, this.tableList)
    const itemsWithDuration = itemConfigs.map(itemConfig => {
      const theTable:TableConfig = this.tableList.filter(
        table => table.tableName === itemConfig.tableName,
      )[0]
    })
  }
}
